﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

namespace APP.HELPER.Utils
{
    public class IconExtractor : IDisposable
    {
        [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Auto)]
        private delegate bool EnumResNameProc(IntPtr hModule, int lpszType, IntPtr lpszName, IconExtractor.IconResInfo lParam);
        private class IconResInfo
        {
            public List<IconExtractor.ResourceName> IconNames = new List<IconExtractor.ResourceName>();
        }
        private class ResourceName
        {
            private IntPtr _bufPtr = IntPtr.Zero;
            public IntPtr Id
            {
                get;
                private set;
            }
            public string Name
            {
                get;
                private set;
            }
            public ResourceName(IntPtr lpName)
            {
                if ((uint)((int)lpName) >> 16 == 0u)
                {
                    this.Id = lpName;
                    this.Name = null;
                }
                else
                {
                    this.Id = IntPtr.Zero;
                    this.Name = Marshal.PtrToStringAuto(lpName);
                }
            }
            public IntPtr GetValue()
            {
                IntPtr result;
                if (this.Name == null)
                {
                    result = this.Id;
                }
                else
                {
                    this._bufPtr = Marshal.StringToHGlobalAuto(this.Name);
                    result = this._bufPtr;
                }
                return result;
            }
            public void Free()
            {
                if (this._bufPtr != IntPtr.Zero)
                {
                    try
                    {
                        Marshal.FreeHGlobal(this._bufPtr);
                    }
                    catch
                    {
                    }
                    this._bufPtr = IntPtr.Zero;
                }
            }
        }
        private const int LOAD_LIBRARY_AS_DATAFILE = 2;
        private const int RT_ICON = 3;
        private const int RT_GROUP_ICON = 14;
        private const int MAX_PATH = 260;
        private const int ERROR_SUCCESS = 0;
        private const int ERROR_FILE_NOT_FOUND = 2;
        private const int ERROR_BAD_EXE_FORMAT = 193;
        private const int ERROR_RESOURCE_TYPE_NOT_FOUND = 1813;
        private const int sICONDIR = 6;
        private const int sICONDIRENTRY = 16;
        private const int sGRPICONDIRENTRY = 14;
        private IntPtr _hModule = IntPtr.Zero;
        private IconExtractor.IconResInfo _resInfo = null;
        private Icon[] _iconCache = null;
        private string _filename = null;
        public string Filename
        {
            get
            {
                return this._filename;
            }
        }
        public int IconCount
        {
            get
            {
                return this._resInfo.IconNames.Count;
            }
        }
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr LoadLibrary(string lpFileName);
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, int dwFlags);
        [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
        private static extern bool FreeLibrary(IntPtr hModule);
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int GetModuleFileName(IntPtr hModule, StringBuilder lpFilename, int nSize);
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool EnumResourceNames(IntPtr hModule, int lpszType, IconExtractor.EnumResNameProc lpEnumFunc, IconExtractor.IconResInfo lParam);
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, int lpType);
        [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
        private static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);
        [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
        private static extern IntPtr LockResource(IntPtr hResData);
        [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
        private static extern int SizeofResource(IntPtr hModule, IntPtr hResInfo);
        public IconExtractor(string filename)
        {
            if (filename == null)
            {
                throw new ArgumentNullException("filename");
            }
            this._hModule = IconExtractor.LoadLibrary(filename);
            if (this._hModule == IntPtr.Zero)
            {
                this._hModule = IconExtractor.LoadLibraryEx(filename, IntPtr.Zero, 2);
                if (this._hModule == IntPtr.Zero)
                {
                    int lastWin32Error = Marshal.GetLastWin32Error();
                    if (lastWin32Error == 2)
                    {
                        throw new FileNotFoundException("Specified file '" + filename + "' not found.");
                    }
                    if (lastWin32Error != 193)
                    {
                        throw new Win32Exception();
                    }
                    throw new ArgumentException("Specified file '" + filename + "' is not an executable file or DLL.");
                }
            }
            StringBuilder stringBuilder = new StringBuilder(260);
            int moduleFileName = IconExtractor.GetModuleFileName(this._hModule, stringBuilder, stringBuilder.Capacity + 1);
            if (moduleFileName != 0)
            {
                this._filename = stringBuilder.ToString();
            }
            else
            {
                int lastWin32Error = Marshal.GetLastWin32Error();
                if (lastWin32Error != 0)
                {
                    throw new Win32Exception();
                }
                this._filename = filename;
            }
            this._resInfo = new IconExtractor.IconResInfo();
            if (!IconExtractor.EnumResourceNames(this._hModule, 14, new IconExtractor.EnumResNameProc(this.EnumResNameCallBack), this._resInfo))
            {
                throw new Win32Exception();
            }
            this._iconCache = new Icon[this.IconCount];
        }
        ~IconExtractor()
        {
            this.Dispose();
        }
        public void Dispose()
        {
            if (this._hModule != IntPtr.Zero)
            {
                try
                {
                    IconExtractor.FreeLibrary(this._hModule);
                }
                catch
                {
                }
                this._hModule = IntPtr.Zero;
            }
            if (this._iconCache != null)
            {
                Icon[] iconCache = this._iconCache;
                for (int i = 0; i < iconCache.Length; i++)
                {
                    Icon icon = iconCache[i];
                    if (icon != null)
                    {
                        try
                        {
                            icon.Dispose();
                        }
                        catch
                        {
                        }
                    }
                }
                this._iconCache = null;
            }
        }
        public Icon GetIcon(int iconIndex)
        {
            if (this._hModule == IntPtr.Zero)
            {
                throw new ObjectDisposedException("IconExtractor");
            }
            if (iconIndex < 0 || this.IconCount <= iconIndex)
            {
                throw new ArgumentException("iconIndex is out of range. It should be between 0 and " + (this.IconCount - 1).ToString() + ".");
            }
            if (this._iconCache[iconIndex] == null)
            {
                this._iconCache[iconIndex] = this.CreateIcon(iconIndex);
            }
            return (Icon)this._iconCache[iconIndex].Clone();
        }
        public static Icon[] SplitIcon(Icon icon)
        {
            if (icon == null)
            {
                throw new ArgumentNullException("icon");
            }
            byte[] array = null;
            using (MemoryStream memoryStream = new MemoryStream())
            {
                icon.Save(memoryStream);
                array = memoryStream.ToArray();
            }
            List<Icon> list = new List<Icon>();
            int num = (int)BitConverter.ToInt16(array, 4);
            for (int i = 0; i < num; i++)
            {
                using (MemoryStream memoryStream2 = new MemoryStream())
                {
                    using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream2))
                    {
                        binaryWriter.Write(array, 0, 4);
                        binaryWriter.Write(1);
                        binaryWriter.Write(array, 6 + 16 * i, 12);
                        binaryWriter.Write(22);
                        int count = BitConverter.ToInt32(array, 6 + 16 * i + 8);
                        int index = BitConverter.ToInt32(array, 6 + 16 * i + 12);
                        binaryWriter.Write(array, index, count);
                        memoryStream2.Seek(0L, SeekOrigin.Begin);
                        list.Add(new Icon(memoryStream2));
                    }
                }
            }
            return list.ToArray();
        }
        public override string ToString()
        {
            return string.Format("IconExtractor (Filename: '{0}', IconCount: {1})", this.Filename, this.IconCount);
        }
        private bool EnumResNameCallBack(IntPtr hModule, int lpszType, IntPtr lpszName, IconExtractor.IconResInfo lParam)
        {
            if (lpszType == 14)
            {
                lParam.IconNames.Add(new IconExtractor.ResourceName(lpszName));
            }
            return true;
        }
        private Icon CreateIcon(int iconIndex)
        {
            byte[] resourceData = this.GetResourceData(this._hModule, this._resInfo.IconNames[iconIndex], 14);
            Icon result;
            using (MemoryStream memoryStream = new MemoryStream())
            {
                using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream))
                {
                    int num = (int)BitConverter.ToUInt16(resourceData, 4);
                    int num2 = 6 + 16 * num;
                    binaryWriter.Write(resourceData, 0, 6);
                    for (int i = 0; i < num; i++)
                    {
                        binaryWriter.BaseStream.Seek((long)(6 + 16 * i), SeekOrigin.Begin);
                        binaryWriter.Write(resourceData, 6 + 14 * i, 12);
                        binaryWriter.Write(num2);
                        IntPtr lpName = (IntPtr)((int)BitConverter.ToUInt16(resourceData, 6 + 14 * i + 12));
                        byte[] resourceData2 = this.GetResourceData(this._hModule, lpName, 3);
                        binaryWriter.BaseStream.Seek((long)num2, SeekOrigin.Begin);
                        binaryWriter.Write(resourceData2, 0, resourceData2.Length);
                        num2 += resourceData2.Length;
                    }
                    memoryStream.Seek(0L, SeekOrigin.Begin);
                    result = new Icon(memoryStream);
                }
            }
            return result;
        }
        private byte[] GetResourceData(IntPtr hModule, IntPtr lpName, int lpType)
        {
            IntPtr intPtr = IconExtractor.FindResource(hModule, lpName, lpType);
            if (intPtr == IntPtr.Zero)
            {
                throw new Win32Exception();
            }
            IntPtr intPtr2 = IconExtractor.LoadResource(hModule, intPtr);
            if (intPtr2 == IntPtr.Zero)
            {
                throw new Win32Exception();
            }
            IntPtr intPtr3 = IconExtractor.LockResource(intPtr2);
            if (intPtr3 == IntPtr.Zero)
            {
                throw new Win32Exception();
            }
            int num = IconExtractor.SizeofResource(hModule, intPtr);
            if (num == 0)
            {
                throw new Win32Exception();
            }
            byte[] array = new byte[num];
            Marshal.Copy(intPtr3, array, 0, array.Length);
            return array;
        }
        private byte[] GetResourceData(IntPtr hModule, IconExtractor.ResourceName name, int lpType)
        {
            byte[] resourceData;
            try
            {
                IntPtr value = name.GetValue();
                resourceData = this.GetResourceData(hModule, value, lpType);
            }
            finally
            {
                name.Free();
            }
            return resourceData;
        }
    }
}
